package br.com.vexillum.control.persistence;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.LogicalExpression;
import org.hibernate.criterion.Restrictions;

import br.com.vexillum.control.manager.ExceptionManager;
import br.com.vexillum.model.CommonEntity;
import br.com.vexillum.util.ReflectionUtils;
import br.com.vexillum.util.Return;

public class GenericPersistence<E extends CommonEntity> implements IGenericPersistence<CommonEntity> {

	private Session session;
	
	public GenericPersistence(){
		this.session = ConnectionFactory.getSession();
		//this.classEntity
	}
	
	public Transaction getTransaction(){
		return session.getTransaction();
	}
	
	public void beginTransaction(){
		if(!getTransaction().isActive()){
			session.getTransaction().begin();
		}		
	}
	
	private void commitTransaction(){
		session.getTransaction().commit();
	}
	
	private void rollbacktTransaction(){
		session.getTransaction().rollback();
	}
	
	@SuppressWarnings("unused")
	private void closeSession(){
		if(session.isOpen()){
			session.close();
		}
	}
	
	@Override
	public Return save(CommonEntity entity) {
		Return ret = null;
		try {
			session.clear();
			beginTransaction();
			session.save(entity);
			commitTransaction();
			ret =  new Return(true);
		} catch (Exception e) {
			rollbacktTransaction();
			ret = new ExceptionManager(e).treatException();
		}		
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Return searchByCriteria(CommonEntity entity) {
		Return ret = null;
		List<CommonEntity> lista = new ArrayList<CommonEntity>();
        Criteria crit = session.createCriteria(entity.getClass());   
        
        try{
	        List<Field> listFields = ReflectionUtils.getSearchFieldsNotNull(entity);
	        
	        Criterion[] cond = new Criterion[listFields.size()];
	
	        for(int i = 0; i < listFields.size() ; i++){
	        	Field field = listFields.get(i);
        		if(!(field.get(entity) instanceof Long || field.get(entity) instanceof Integer || field.get(entity) instanceof CommonEntity || field.get(entity) instanceof Boolean)){
	        		cond[i] = Restrictions.like(field.getName() , "%" + field.get(entity) + "%");
	        	} else {
	        		cond[i] = Restrictions.eq(field.getName() , field.get(entity));
	        	}
	        }
	        
	        LogicalExpression expr = null;
	        
	        if(cond.length > 1){
	            expr = null;
	            for(int i = 1; i < cond.length; i++){
	                if(expr == null){
	                        expr = Restrictions.and(cond[0], cond[1]);
	                } else {
	                        expr = Restrictions.and(expr, cond[i]);
	                }
	            }
	            crit.add(expr);
	        } else {
	            crit.add(cond[0]);
	        }
	        
	        lista.addAll(crit.list());   
	        ret = new Return(true, lista);
        } catch (Exception e){        	
        	ret = new ExceptionManager(e).treatException();
        }
        return ret;
	}

	@Override
	public Return listAll(Class<CommonEntity> classEntity) {
		Return ret = null;
		List<?> list = null;
		try {
			list = session.createCriteria(classEntity).list();
			ret = new Return(true, list);
		} catch (Exception e) {
			ret = new ExceptionManager(e).treatException();
		}
		return ret;
	}	

	@Override
	public Return delete(CommonEntity entity) {
		Return ret = null;
		try {
			beginTransaction();		
			session.delete(entity);
			commitTransaction();
			ret = new Return(true);
		} catch (Exception e) {
			rollbacktTransaction();
			ret = new ExceptionManager(e).treatException();
		}
		return ret;
	}

	@Override
	public Return update(CommonEntity entity) {
		Return ret = null;
		try {
			session.clear();
			beginTransaction();
			session.update(entity);
			commitTransaction();
			ret = new Return(true);
		} catch (Exception e) {
			rollbacktTransaction();
        	ret = new ExceptionManager(e).treatException();
		}	
		return ret;
	}

	@Override
	public Return searchByHQL(CommonEntity entity) {
		// TODO Criar pesqusia pela HQL usando a entidade
		return null;
	}

	@Override
	public Return searchByHQL(String hql) {
		Return ret = null;
		try {
			ret = new Return(true, session.createQuery(hql).list());
		} catch (Exception e) {
			ret = new ExceptionManager(e).treatException();
		}
		return ret;
	}

	@Override
	public Return searchByNativeSQL(String sql) {
		// TODO Criar pesqusia pela SQL nativa
		return null;
	}

}
